#ifndef __I_PTR_H__
#define __I_PTR_H__

#include "Ink_atomic.h"

class RefCountObj {
public:
	RefCountObj() : m_refcount(0) {}
	RefCountObj(const RefCountObj &s) : m_refcount(0)
	{
		(void)s;
		return;
	}
	RefCountObj &operator=(const RefCountObj &s)
	{
		(void)s;
		return (*this);
	}
	virtual ~RefCountObj() {}

	int refcount_inc()
	{
		return ink_atomic_increment((int *)&m_refcount, 1) + 1;
	}

	int refcount_dec()
	{
		return ink_atomic_increment((int *)&m_refcount, -1) - 1;
	}

	int refcount() const
	{
		return m_refcount;
	}

	virtual void free()
	{
		delete this;
	}

	volatile int m_refcount;
};


template <typename T>
class Ptr{
public:
	explicit Ptr(T *ptr = 0);
	Ptr(const Ptr<T> &);
	Ptr<T> &operator=(const Ptr<T> &);
	Ptr<T> &operator=(T *);
	~Ptr();

	T * to_ptr();
	void clear();
	RefCountObj * _ptr()
	{
		return (RefCountObj *)m_ptr;
	}
	
	operator T *()  const { return (m_ptr); }
	T *operator->() const { return (m_ptr); }
	T &operator*()  const { return (*m_ptr); }

	int operator==(const T *p) { return (m_ptr == p); }
	int operator==(const Ptr<T> &p) { return (m_ptr == p.m_ptr); }
	int operator!=(const T *p) { return (m_ptr != p); }
	int operator!=(const Ptr<T> &p) { return (m_ptr != p.m_ptr); }
 
	T *m_ptr;
};

template <typename T>
inline Ptr<T>::Ptr(T *ptr /* = 0 */) : m_ptr(ptr)
{
	if(m_ptr)
		_ptr()->refcount_inc();
	return;
}

template <typename T>
inline Ptr<T>::Ptr(const Ptr<T> &src) : m_ptr(src.m_ptr)
{
	if(m_ptr)
		_ptr()->refcount_inc();
	return;
}

template <typename T>
inline Ptr<T>::~Ptr()
{
	if(m_ptr && _ptr()->refcount_dec() == 0)
		_ptr()->free();
	return;
}

template <typename T>
inline Ptr<T> &Ptr<T>::operator=(T *p)
{
	T *tmp_ptr = m_ptr;

	if(m_ptr == p)
		return (*this);

	m_ptr = p;

	if(m_ptr)
		_ptr()->refcount_inc();

	if(tmp_ptr && tmp_ptr->refcount_dec() == 0)
		tmp_ptr->free();

	return (*this);	
}

template <typename T>
inline Ptr<T> &Ptr<T>::operator=(const Ptr<T> &src)
{
	return (operator=(src.m_ptr));
}

template <typename T>
inline void Ptr<T>::clear()
{
	if(m_ptr) {
		if(_ptr()->refcount_dec())
			_ptr()->free();
		m_ptr = 0;
	}
}

#endif
